package twoD.hofem.Q;

import twoD.hofem.HierarchicFBOnR2ToR;
import twoD.hofem.LinearCombinationR2ToRn;
import inf.math.LinAlg;

/*
 * @author Group Malamat
 *
 */

/*
 * Face is a 2 dimensional mesh part.It is associated with
 * four nodes and four edges.
 */
public class Face extends MeshPartP {

	private Edge[] edges = new Edge[4];
	private BlendingFunctionR2ToR2 geometry;
	private Node[] nodes = new Node[4];
	private final HierarchicFBOnR2ToR basis = new HierarchicFBOnR2ToR(1);

	/*
	 * Constructs a new face using the specified edges.
	 * 
	 * @param edges 
	 * 				edges
	 */
	public Face(Edge e1, Edge e2, Edge e3, Edge e4) {
		edges[0] = e1;
		edges[1] = e2;
		edges[2] = e3;
		edges[3] = e4;
		nodes[0] = edges[3].getCommonNode(edges[0]);
		nodes[1] = edges[0].getCommonNode(edges[1]);
		nodes[2] = edges[1].getCommonNode(edges[2]);
		nodes[3] = edges[2].getCommonNode(edges[3]);
		geometry = new BlendingFunctionR2ToR2(new EdgeFunction[] {
				edges[0].getGeometry(), edges[1].getGeometry(),
				edges[2].getGeometry(), edges[3].getGeometry() });
		basis.setEdgesOrientation(geometry.getEdgeOrientations());
	}

	@Override
	public int getP() {
		return basis.getP();
	}

	@Override
	public void setP(int p) {
		basis.setP(p);
	}

	@Override
	public int countModes() {
		int a = getP() - 1;
		return a * a;
	}

	@Override
	protected int[][] getAllDofNumbers() {
		int[][] res = new int[9][];

		for (int i = 0; i < 4; i++) {
			res[i] = nodes[i].getDofNumbers();
			int[][] a = edges[i].getAllDofNumbers();
			res[i + 4] = a[2];
		}
		res[8] = getDofNumbers();
		return res;
	}

	/*
	 * Returns to the geometry of the face.
	 */
	public BlendingFunctionR2ToR2 getGeometry() {
		return geometry;
	}

	/*
	 * Returns to the specified node.
	 * 
	 * @param idx 
	 * 			  index of node
	 */
	public Node getNode(int idx) {
		return nodes[idx];
	}

	/*
	 * Returns to the approximate solution of this element.
	 */
	public LinearCombinationR2ToRn getSolution() {
		LinearCombinationR2ToRn lincom = new LinearCombinationR2ToRn(getD(),
				basis);
		lincom.setCoefficients(getULocal());
		return lincom;
	}

	/*
	 * Returns to the specified edge.
	 * 
	 * @param idx 
	 * 			   index of edge
	 */
	public Edge getEdge(int idx) {
		return edges[idx];
	}

	public HierarchicFBOnR2ToR getBasis() {
		return basis;
	}

	/*
	 * Computes shape functions values at the specified point.
	 * 
	 * @param xi1 => first local parameter
	 * 
	 * @param xi2 => second local parameter
	 * 
	 * @return function values (res)
	 */
	public double[] valuesAt(double xi1, double xi2) {
		int k = basis.getDimension();
		double[] res = new double[k];
		for (int i = 0; i < k; i++) {
			res[i] = basis.getBasisFunction(i).valueAt(xi1, xi2);
		}
		return res;
	}

	/*
	 * Computes gradients of the shape functions with respect to physical
	 * coordinates. Arrangement is in a matrix.
	 * 
	 * <pre> | dN1/dx1 dN1/dx2 | | dN2/dx1 dN2/dx2 | | ... | </pre>
	 * 
	 * @param xi1 => first local parameter
	 * 
	 * @param xi2 => second local parameter
	 * 
	 * @return global derivatives
	 */
	public double[][] gGradientsAt(double xi1, double xi2) {
		int n = basis.getDimension();
		double[][] gradients = new double[n][2];
		double[][] invJac = geometry.jacobianAt(xi1, xi2);

		LinAlg.invert2x2(invJac);
		for (int i = 0; i < n; i++) {
			double[] lGrad = basis.getBasisFunction(i).gradientAt(xi1, xi2);
			LinAlg.multiply(false, 2, 2, 1.0, invJac, lGrad, 0.0, gradients[i]);
		}
		return gradients;
	}
}
